﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using HAKGERSoft.Synapse;
using System.Xml.Linq;
using System.Threading;

namespace HAKGERSoft.Synapse.Samples {
    
    public partial class Form1:Form {
        
        ConvolutionalNetwork Network;
        private KohonenNetwork KohonenNetwork;

        List<TrainingItem> TestSet = new List<TrainingItem>();
        List<TrainingItem> SomTrainingSet = new List<TrainingItem>();
        List<TrainingItem> ConvTrainingSet = new List<TrainingItem>();
        double[][] outputs;

        //private const int PictureSize = 92;
        private const int NumberOfClasses = 10;

        #region SOM Configuration
        
        private const int SomWindowsSize = 5;
        private const int SomCyclesNo = 250;
        private const int SomNeighborhood = 15;
        private const double  SomNChangingRate = 0.999;
        private const double SomLChangingRate = 0.999;
        private const double SomLearningRate = 0.1;
        private const int SomStep = 4;
        
        #endregion 

        #region Convolutional Configuration

        private const double LearningFactor = 0.05;
        private const double ConvLChangingRate = 0.99;
        private const int NumberOfCycles = 100;

        #endregion

        const string ImagesFilter = "jpg files (*.jpg)|*.jpg";

        bool Loaded=false;
        bool Stopped=true;
        
        double[] input;
        
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Load(object sender,EventArgs e) {
            outputs = CreateOutputs();
            BuildNetwork();
            BuildKohonen();
        }

        private void BuildKohonen()
        {
            KohonenNetwork = new KohonenNetwork(SomWindowsSize*SomWindowsSize, SomWindowsSize, SomWindowsSize, 
                SomCyclesNo, SomNeighborhood, SomNChangingRate, SomLearningRate, SomLChangingRate);
        }

        private double[][] CreateOutputs()
        {
            double[][] ret = new double[NumberOfClasses][];

            for (int i = 0; i < NumberOfClasses; i++)
            {
                ret[i] = new double[NumberOfClasses];
                ret[i][i] = 0.8;
            }

            return ret;
        }

        void BuildNetwork()
        {
            int[] layers=new int[] {
                968,8000,2000,1600,400,NumberOfClasses
            };
            LayerConnector[] map=new LayerConnector[] {
                new ConvolutionAuto(3,20,2,2),
                new ConvolutionAuto(2,20,20,0),//new SubSampling(20),
                new ConvolutionAuto(3,25,20,2),
                new ConvolutionAuto(2,25,25,0),//new SubSampling(25),
                //new FullLayerConnector(),
                new FullLayerConnector()
            };
            ConvolutionalTopology topology=new ConvolutionalTopology(layers,1,map,new HyperbolicTangent());
            ConvolutionalGenerator generator=new ConvolutionalGenerator();
            Network=generator.Create(topology);
            Network.LearnFactor = LearningFactor;
            Network.Reset(-0.1,0.1);
        }

        private void button3_Click(object sender,EventArgs e)//load network
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.CheckFileExists=true;
            dlg.CheckPathExists=true;
            dlg.Filter="xml files (*.xml)|*.xml";
            dlg.Multiselect=false;
             if (dlg.ShowDialog()==DialogResult.OK) {
                 ConvolutionalXMLSerializer serializer=new ConvolutionalXMLSerializer();
                 Network=serializer.Deserialize(XDocument.Load(dlg.FileName));
                 Network.LearnFactor=LearningFactor;
                 Loaded=true;
             }
        }

        private void button4_Click(object sender,EventArgs e) //save network
        {
            Save();
        }

        private void Save(string file = "FaceRecognition.xml")
        {
            ConvolutionalXMLSerializer serializer=new ConvolutionalXMLSerializer();
            XDocument doc=serializer.Serialize(Network);
            doc.Save(file);
        }

        private void button5_Click(object sender, EventArgs e)     //start/stop learning
        {
            if(Stopped)
                StartLearning();
            else
                StopLearning();
        }

        void StartLearning(){
            Stopped=false;
            button5.Text="Stop";
            Log.Clear();
            Thread.CurrentThread.Priority=ThreadPriority.Lowest;

            LearnKohonen();
            ConvTrainingSet = KohonenNetwork.CreateOutputImages(SomTrainingSet, SomWindowsSize, SomStep);

            for (int idx=0; idx < NumberOfCycles; idx++)
            {
                List<TrainingData> trainingList=new List<TrainingData>();
                foreach(var input in ConvTrainingSet) 
                {
                    if (Stopped)
                        return; 
                    Application.DoEvents();
                    trainingList.Add(new TrainingData(input.Image, outputs[input.ClassNumber]));
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }
                Network.LearnFactor *= ConvLChangingRate;
                BPRequest request = new BPRequest(trainingList.ToArray(),1) { ShuffleTrainingSet=true };
                BPResponse response = Network.BP(request);
                double eps=response.Epochs[0].Epsilon;
                double ms=response.BPTime.TotalMilliseconds;
                Log.Text+=string.Format("Process cycle {0}, error={1}, time[ms]={2}\r\n",idx.ToString(),eps.ToString("0.000"),ms.ToString("0.000"));
                trainingList.Clear(); 
            }
            //Save(@"D:\Users\Kamil\Documents\Dropbox\II sem\SN\ORL\FaceRecognition.xml");
            StopLearning();
        }

        private void LearnKohonen()
        {
            var list = SomTrainingSet.ToList();
            list.AddRange(TestSet);
            KohonenNetwork.SetInput(FileUtil.CreateSomInput(list,SomWindowsSize,SomStep));
            KohonenNetwork.Learn(true,false);
        }

        void StopLearning(){
            Stopped=true;
            button5.Text="Start";
        }

        private void button1_Click(object sender, EventArgs e)//load input file to recognize
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.CheckFileExists = true;
            dlg.CheckPathExists = true;
            dlg.Filter = ImagesFilter;
            dlg.Multiselect = false;
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                input = FileUtil.getInput(dlg.FileName);
                pictureBox1.Image = FileUtil.getImage(dlg.FileName);
            }
            
            
        }

        private void button2_Click(object sender, EventArgs e)//recognize image
        {            
            if (input == null || input.Count() == 0)
            {
                MessageBox.Show("Load input first!!!");
                return;
            }
            var list = new List<TrainingItem>();
            list.Add(new TrainingItem(-1, input));
            var SomImg = KohonenNetwork.CreateOutputImages(list, SomWindowsSize, SomStep);
            double[] output = Network.Pulse(SomImg[0].Image);
            double maxValue = -100;
            int outClassNumber = -1;
            textBox1.Clear();
            var outputStrings = new string[output.Length];
            for (int i = 0; i < output.Length; i++)
            {
                if (output[i] > maxValue)
                {
                    maxValue = output[i];
                    outClassNumber = i;
                }

                outputStrings[i] = "[" + i + "]: " + output[i];
            }
            label3.Text = outClassNumber.ToString();
            label4.Text = maxValue.ToString();
            textBox1.Lines = outputStrings;

            var ret = SomTrainingSet.Where(x => x.ClassNumber == outClassNumber);

            if (ret.Count() > 0)
            {
                double[] outputImage = SomTrainingSet.Where(x => x.ClassNumber == outClassNumber).First().Image;
                pictureBox2.Image = FileUtil.getImageFromDoubles(outputImage);
            }
        }

        private void button6_Click(object sender, EventArgs e)//load training set
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.CheckFileExists = true;
            dlg.CheckPathExists = true;
            dlg.Filter = ImagesFilter;
            dlg.Multiselect = true;
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                foreach (var file in dlg.FileNames)
                {
                    SomTrainingSet.Add(FileUtil.getTrainingItem(file));
                }
            }
        }

        private void button7_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.CheckFileExists = true;
            dlg.CheckPathExists = true;
            dlg.Filter = ImagesFilter;
            dlg.Multiselect = true;
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                foreach (var file in dlg.FileNames)
                {
                    TestSet.Add(FileUtil.getTrainingItem(file));
                }
            }
        }
    }
}
